package com.sky.service.impl;

import com.alibaba.fastjson2.JSON;
import com.sky.constant.SearchESConstant;
import com.sky.dto.es.ProductModel;
import com.sky.service.ProductSearchService;
import com.sky.utils.StringUtils;
import com.sky.vo.SearchParam;
import com.sky.vo.SearchProductResponse;
import jakarta.annotation.Resource;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.rescore.QueryRescorerBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class ProductSearchServiceImpl implements ProductSearchService {
    @Resource
    private RestHighLevelClient restHighLevelClient;

    @Override
    public boolean addProductSkuInfo(List<ProductModel> productModels) throws IOException {
        BulkRequest bulkRequest = new BulkRequest();
        if (StringUtils.isNotEmpty(productModels)){
            productModels.forEach(productModel -> {
                IndexRequest indexRequest = new IndexRequest(SearchESConstant.PRODUCT_INDEX);
                indexRequest.id(productModel.getSkuId().toString());
                indexRequest.source(JSON.toJSONString(productModel),XContentType.JSON);
                bulkRequest.add(indexRequest);
            });
        }
        BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        bulkResponse.iterator().forEachRemaining(item->{
            System.out.println("商品" + item.getId() + "入库成功！");
        });
        return !bulkResponse.hasFailures();
    }

    @Override
    public SearchProductResponse searchProduct(SearchParam searchParam) {
        SearchRequest searchRequest = buildQuery(searchParam);
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            SearchProductResponse searchProductResponses = buildResponseResult(searchParam,searchResponse);
            return searchProductResponses;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    private SearchRequest buildQuery(SearchParam searchParam) {
        // 1、构造查询请求
        SearchRequest searchRequest = new SearchRequest(SearchESConstant.PRODUCT_INDEX);

        // 2、构造查询请求构造器
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        // 4、bool 查询
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        // 5.1 bool查询下面的must查询器 -- 模糊匹配
        if (StringUtils.isNotEmpty(searchParam.getKeyword())){
            MatchQueryBuilder skuTitleQueryBuilder = QueryBuilders.matchQuery("skuTitle", searchParam.getKeyword());
            boolQueryBuilder.must(skuTitleQueryBuilder);
        }

        // 5.2 bool查询下面的filter过滤器 -- 按照三级分类id查询
        if (StringUtils.isNotEmpty(searchParam.getCatalog3Id())){
            TermQueryBuilder catalogIdQueryBuilder = QueryBuilders.termQuery("catalogId", searchParam.getCatalog3Id());
            boolQueryBuilder.filter(catalogIdQueryBuilder);
        }

        // 5.3 bool查询下面的filter过滤器 -- 按照品牌id查询
        if (StringUtils.isNotEmpty(searchParam.getBrandId())){
            TermsQueryBuilder brandIdQueryBuilder = QueryBuilders.termsQuery("brandId", searchParam.getBrandId());
            boolQueryBuilder.filter(brandIdQueryBuilder);
        }

        // 5.4 bool查询下面的filter过滤器 -- 查询是否有库存
        if (StringUtils.isNotEmpty(searchParam.getHasStock())){
            TermQueryBuilder hasStockQueryBuilder = QueryBuilders.termQuery("hasStock", searchParam.getHasStock() == 1);
            boolQueryBuilder.filter(hasStockQueryBuilder);
        }

        // 5.5 bool查询下面的filter过滤器，下面的嵌入式属性 -- 按照所有的属性查询
        if (StringUtils.isNotEmpty(searchParam.getAttrs())){
            // attrs的入参格式为：&attrs=1_5寸:8寸&attrs=2_16G:8G
            List<String> attrs = searchParam.getAttrs();
            attrs.forEach(attr -> {
                BoolQueryBuilder boolNestedQueryBuilder = QueryBuilders.boolQuery();
                String[] attrInfo = attr.split("_");
                String attrId = attrInfo[0];
                String attrValues = attrInfo[1];
                String[] attrValue = attrValues.split(":");

                TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("attrs.attrId", attrId);
                boolNestedQueryBuilder.must(termQueryBuilder);

                // 5.5.1 嵌入式属性的must查询
                TermsQueryBuilder termsQueryBuilder = QueryBuilders.termsQuery("attrs.attrValue", attrValue);
                boolNestedQueryBuilder.must(termsQueryBuilder);

                // 5.5.2 嵌入式属性的filter过滤器(每一个属性条件查询都应该单独生成一条嵌入式的Nested)
                NestedQueryBuilder nestedQueryBuilder = new NestedQueryBuilder("attrs", boolNestedQueryBuilder, ScoreMode.None);
                boolQueryBuilder.filter(nestedQueryBuilder);
            });
        }

        // 5.6 bool查询下面的filter过滤器 -- 按照价格区间查询
        String skuPrice = searchParam.getSkuPrice();
        if (StringUtils.isNotEmpty(skuPrice)){
            // skuPrice的入参格式为：1_500/_500/500_
            String[] prices = skuPrice.split("_");
            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("skuPrice");
            if (skuPrice.startsWith("_")){
                rangeQueryBuilder.lte(prices[1]).gte(0);
            } else if (skuPrice.endsWith("_")){
                rangeQueryBuilder.gte(prices[0]);
            } else {
                rangeQueryBuilder.gte(prices[0]).lte(prices[1]);
            }
            boolQueryBuilder.filter(rangeQueryBuilder);
        }

        // 3、构造查询请求构造器
        searchSourceBuilder.query(boolQueryBuilder);

        // 6、排序
        if (StringUtils.isNotEmpty(searchParam.getSort())){
            // sort的入参格式为：sort=hotScore_asc
            String sort = searchParam.getSort();
            String[] sortInfos = sort.split("_");
            String sortField = sortInfos[0];
            String sortOrder = sortInfos[1];
            switch (sortOrder){
                case "asc":
                    searchSourceBuilder.sort(sortField, SortOrder.ASC);
                    break;
                case "desc":
                    searchSourceBuilder.sort(sortField, SortOrder.DESC);
                    break;
            }
        }

        // 7、分页
        searchSourceBuilder.from((searchParam.getPageNum()-1) * searchParam.getPageSize());
        searchSourceBuilder.size(searchParam.getPageSize());

        // 8、搜索关键字高亮
        if (StringUtils.isNotEmpty(searchParam.getKeyword())){
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("skuTitle");
            highlightBuilder.preTags("<b style='color:red'>");
            highlightBuilder.postTags("</b>");
            searchSourceBuilder.highlighter(highlightBuilder);
        }

        // 9.1 聚合品牌信息（品牌id -》 品牌名称、品牌图片）
        AggregationBuilder brandAggregationBuilder = AggregationBuilders.terms("brand_agg").field("brandId").size(SearchESConstant.SEARCH_AGGREGATION_SIZE);
        brandAggregationBuilder.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName").size(SearchESConstant.SEARCH_AGGREGATION_SIZE));
        brandAggregationBuilder.subAggregation(AggregationBuilders.terms("brand_img_agg").field("skuImg").size(SearchESConstant.SEARCH_AGGREGATION_SIZE));
        searchSourceBuilder.aggregation(brandAggregationBuilder);

        // 9.2 聚合分类信息（分类id -》 分类名称）
        AggregationBuilder catalogAggregationBuilder = AggregationBuilders.terms("catalog_agg").field("catalogId").size(SearchESConstant.SEARCH_AGGREGATION_SIZE);
        catalogAggregationBuilder.subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(SearchESConstant.SEARCH_AGGREGATION_SIZE));
        searchSourceBuilder.aggregation(catalogAggregationBuilder);

        // 9.3 聚合嵌入式属性信息
        NestedAggregationBuilder nestedAttrAggregationBuilder = AggregationBuilders.nested("attrs_agg", "attrs");
        TermsAggregationBuilder nestedAttrIdAggregationBuilder = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId").size(SearchESConstant.SEARCH_AGGREGATION_SIZE);

        // 9.3.1 嵌入式属性的聚合名称、聚合值
        nestedAttrIdAggregationBuilder.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(SearchESConstant.SEARCH_AGGREGATION_SIZE));
        nestedAttrIdAggregationBuilder.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(SearchESConstant.SEARCH_AGGREGATION_SIZE));

        nestedAttrAggregationBuilder.subAggregation(nestedAttrIdAggregationBuilder);
        searchSourceBuilder.aggregation(nestedAttrAggregationBuilder);

        System.out.println("构建的DSL语句：" + searchSourceBuilder);

        searchRequest.source(searchSourceBuilder);

        return searchRequest;
    }


    private SearchProductResponse buildResponseResult(SearchParam searchParam,SearchResponse searchResponse) {
        SearchProductResponse searchProductResponse = new SearchProductResponse();

        // 1、查询出来的商品的数据
        SearchHit[] searchHits = searchResponse.getHits().getHits();
        List<ProductModel> productModels = new ArrayList<>();
        for (SearchHit searchHit : searchHits) {
            ProductModel productModel = JSON.parseObject(searchHit.getSourceAsString(), ProductModel.class);

            // 高亮字段
            Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
            if (StringUtils.isNotEmpty(highlightFields)){
                HighlightField highlightField = highlightFields.get("skuTitle");
                String highlightText = highlightField.getFragments()[0].toString();
                productModel.setSkuTitle(highlightText);
            }

            productModels.add(productModel);
        }
        searchProductResponse.setProducts(productModels);

        // 2、查询结果涉及到的品牌数据
        Terms brandAgg = searchResponse.getAggregations().get("brand_agg");
        List<SearchProductResponse.BrandVo> brandVos = brandAgg.getBuckets().stream().map(item -> {
            SearchProductResponse.BrandVo brandVo = new SearchProductResponse.BrandVo();
            // 品牌id
            brandVo.setBrandId(Long.parseLong(item.getKeyAsString()));
            // 品牌名称
            Terms brandNameAgg = item.getAggregations().get("brand_name_agg");
            String brandName = brandNameAgg.getBuckets().get(0).getKeyAsString();
            brandVo.setBrandName(brandName);
            // 品牌图片
            Terms brandImgAgg = item.getAggregations().get("brand_img_agg");
            String brandImg = brandImgAgg.getBuckets().get(0).getKeyAsString();
            brandVo.setBrandImg(brandImg);
            return brandVo;
        }).collect(Collectors.toList());
        searchProductResponse.setBrands(brandVos);

        // 3、查询结果涉及到的分类数据
        Terms catalogAgg = searchResponse.getAggregations().get("catalog_agg");
        List<SearchProductResponse.CategoryVo> categoryVos = catalogAgg.getBuckets().stream().map(item -> {
            SearchProductResponse.CategoryVo categoryVo = new SearchProductResponse.CategoryVo();
            // 分类id
            categoryVo.setCategoryId(Long.parseLong(item.getKeyAsString()));
            // 分类名称
            Terms catalogNameAgg = item.getAggregations().get("catalog_name_agg");
            String catalogName = catalogNameAgg.getBuckets().get(0).getKeyAsString();
            categoryVo.setCategoryName(catalogName);
            return categoryVo;
        }).collect(Collectors.toList());
        searchProductResponse.setCategories(categoryVos);

        // 4、查询结果涉及到的嵌入式属性数据
        ParsedNested attrsAgg = searchResponse.getAggregations().get("attrs_agg");
        ParsedLongTerms attrIdAgg = attrsAgg.getAggregations().get("attr_id_agg");
        List<SearchProductResponse.AttrVo> attrVos = attrIdAgg.getBuckets().stream().map(item -> {
            SearchProductResponse.AttrVo attrVo = new SearchProductResponse.AttrVo();
            // 嵌入式属性的聚合id
            attrVo.setAttrId(Long.parseLong(item.getKeyAsString()));
            // 嵌入式属性的聚合名称
            ParsedStringTerms attrNameAgg = item.getAggregations().get("attr_name_agg");
            attrVo.setAttrName(attrNameAgg.getBuckets().get(0).getKeyAsString());
            // 嵌入式属性的聚合值
            ParsedStringTerms attrValueAgg = item.getAggregations().get("attr_value_agg");
            attrVo.setAttrValues(attrValueAgg.getBuckets().stream().map(MultiBucketsAggregation.Bucket::getKeyAsString).collect(Collectors.toList()));
            return attrVo;
        }).collect(Collectors.toList());
        searchProductResponse.setAttrs(attrVos);

        // 5、查询结果涉及到的分页数据
        searchProductResponse.setPageNum(searchParam.getPageNum());
        // 查询结果的总记录数
        long total = searchResponse.getHits().getTotalHits().value;
        searchProductResponse.setTotal(total);
        // 总页码数
        long totalPage = total % searchParam.getPageSize() == 0 ? total / searchParam.getPageSize() : total / searchParam.getPageSize() + 1;
        searchProductResponse.setTotalPage(totalPage);
        List<Integer> pageNavs = new ArrayList<>();
        for (long i = 0; i < totalPage; i++) {
            pageNavs.add((int) (i + 1));
        }
        searchProductResponse.setPageNavs(pageNavs);

        // 6、查询结果涉及到的导航数据
        String orgRequestUrl = searchParam.getOrgRequestUrl();
        List<String> attrs = searchParam.getAttrs();
        if (StringUtils.isNotEmpty(attrs)){
            List<SearchProductResponse.NavVo> navVoList = attrs.stream().map(attr -> {
                SearchProductResponse.NavVo navVo = new SearchProductResponse.NavVo();
                String[] split = attr.split("_");
                String attrId = split[0];
                SearchProductResponse.AttrVo attrVo = attrVos.stream().filter(data -> data.getAttrId().toString().equals(attrId)).collect(Collectors.toList()).get(0);
                navVo.setNavName(attrVo.getAttrName());
                navVo.setNavValue(split[1]);
                String replace;
                try {
                    String encode = URLEncoder.encode(attr, "UTF-8");
                    encode = encode.replaceAll("\\+", "%20");
                    replace = orgRequestUrl.replace("&attrs=" + encode, "");
                } catch (UnsupportedEncodingException e) {
                    throw new RuntimeException(e);
                }
                navVo.setLink("http://search.skymall.com/list.html?" + replace);
                return navVo;
            }).collect(Collectors.toList());
            searchProductResponse.setNavs(navVoList);
        }


        System.out.println("查询结果：" + JSON.toJSONString(searchProductResponse));

        return searchProductResponse;
    }
}
